package com.lhj.xxsc.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.lhj.xxsc.seckill.interceptor.LoginUserInterceptor;
import com.xxsc.common.to.mq.SeckillOrderTo;
import com.xxsc.common.utils.R;
import com.lhj.xxsc.seckill.feign.CouponFeignService;
import com.lhj.xxsc.seckill.feign.ProductFeignService;
import com.lhj.xxsc.seckill.service.SeckillService;
import com.lhj.xxsc.seckill.to.SecKillSkuRedisTo;
import com.lhj.xxsc.seckill.vo.SeckillSessionWithSkusVo;
import com.lhj.xxsc.seckill.vo.SkuInfoVo;
import com.xxsc.common.vo.MemberRespVo;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author 罗惠江
 * @version 1.0
 * @email 1511619294@qq.com
 * @date 2021/1/12 23:12
 */
@Service
public class SeckillServiceImpl implements SeckillService {
	@Autowired
	CouponFeignService couponFeignService;

	@Autowired
	ProductFeignService productFeignService;

	@Autowired
	StringRedisTemplate redisTemplate;

	@Autowired
	RedissonClient redissonClient;

	@Autowired
	RabbitTemplate rabbitTemplate;

	private static final String SESSIONS_CACHE_PREFIX = "seckill:sessions:";
	private static final String SKUKILL_CACHE_PREFIX = "seckill:skus:";
	private static final String SKU_STOCK_SEMAPHORE = "seckill:stock:";// + 商品随机码

	@Override
	public void uploadSeckillSkuLatest3Days() {
		// 扫描最近三天需要参与的秒杀活动
		R lates3DaySession = couponFeignService.getLates3DaySession();
		if (lates3DaySession.getCode() == 0) {
			List<SeckillSessionWithSkusVo> data = lates3DaySession.getData(new TypeReference<List<SeckillSessionWithSkusVo>>() {
			});
			if (data != null) {
				// 缓存到 redis
				// 缓存活动信息
				saveSessionInfos(data);
				// 参加活动的商品信息
				saveSessionSkuInfos(data);
			}
		}
	}

	@Override
	public List<SecKillSkuRedisTo> getCurrentSeckillSkus() {
		// 判断当前时间属于哪个秒杀场次
		// 获取redis 的场次信息
		Long time = new Date().getTime();
		Set<String> keys = redisTemplate.keys(SESSIONS_CACHE_PREFIX + "*");
		for (String key : keys) {
			String replace = key.replace(SESSIONS_CACHE_PREFIX, "");
			String[] s = replace.split("_");
			Long start = Long.parseLong(s[0]);
			Long end = Long.parseLong(s[1]);
			if (start <= time && time <= end) {
				// 获取这个场次的秒杀商品的所有信息 TODO 有bug 获取 的秒杀商品数量是固定的
				List<String> range = redisTemplate.opsForList().range(key, -100, 100);
				BoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
				List<String> list = ops.multiGet(range);
				if (!CollectionUtils.isEmpty(list)) {
					List<SecKillSkuRedisTo> collect = list.stream().map(item -> {
						SecKillSkuRedisTo redisTo = JSON.parseObject(item.toString(), SecKillSkuRedisTo.class);
						//  redisTo.setRandomCode("");
						return redisTo;
					}).collect(Collectors.toList());
					return collect;
				}
				break;
			}
		}
		return null;
	}

	@Override
	public SecKillSkuRedisTo getSkuSeckillInfo(Long skuId) {
		// 找到所有 参与的秒杀商品的key
		BoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
		Set<String> keys = ops.keys();
		if (!CollectionUtils.isEmpty(keys)) {
			String regx = "\\d_" + skuId;
			for (String key : keys) {
				if (Pattern.matches(regx, key)) {
					String s = ops.get(key);
					SecKillSkuRedisTo redisTo = JSON.parseObject(s, SecKillSkuRedisTo.class);
					// 处理随机码
					Long startTime = redisTo.getStartTime();
					Long endTime = redisTo.getEndTime();
					Long time = new Date().getTime();
					if (time >= startTime && time <= endTime) {

					} else {
						redisTo.setRandomCode(null);
					}
					return redisTo;
				}
			}
		}
		return null;
	}


	private void saveSessionInfos(List<SeckillSessionWithSkusVo> session) {
		session.stream().forEach(item -> {
			Long startTime = item.getStartTime().getTime();
			Long endTime = item.getEndTime().getTime();
			String key = SESSIONS_CACHE_PREFIX + startTime + "_" + endTime;
			// 缓存活动信息
			Boolean hasKey = redisTemplate.hasKey(key);
			if (!hasKey) {
				List<String> skusList = item.getRelationSkus().stream().map(skus -> {
					return skus.getPromotionSessionId() + "_" + skus.getSkuId().toString();
				}).collect(Collectors.toList());
				redisTemplate.opsForList().leftPushAll(key, skusList);
			}
		});
	}

	private void saveSessionSkuInfos(List<SeckillSessionWithSkusVo> session) {
		session.stream().forEach(item -> {
			BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
			// 缓存商品信息
			item.getRelationSkus().stream().forEach(skus -> {
				// 商品随机码
				String token = UUID.randomUUID().toString().replace("_", "");
				if (!ops.hasKey(skus.getPromotionSessionId() + "_" + skus.getSkuId().toString())) {
//				String skusStr = JSON.toJSONString(skus);
					SecKillSkuRedisTo redisTo = new SecKillSkuRedisTo();
					// sku 的秒杀信息
					BeanUtils.copyProperties(skus, redisTo);
					// sku 的基本信息
					R r = productFeignService.getProductInfo(skus.getSkuId());
					if (r.getCode() == 0) {
						SkuInfoVo skuInfo = r.getData("skuInfo", new TypeReference<SkuInfoVo>() {
						});
						redisTo.setSkuInfo(skuInfo);
					}
					// 当前商品的时间信息
					redisTo.setStartTime(item.getStartTime().getTime());
					redisTo.setEndTime(item.getEndTime().getTime());
					redisTo.setRandomCode(token);
					String skusStr = JSON.toJSONString(redisTo);
					ops.put(skus.getPromotionSessionId() + "_" + skus.getSkuId().toString(), skusStr);
					// 如果当前这个场次商品库存已经上架，就不要上架了。
					// 使用信号量作为分布式的库存存储 限流
					RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + token);
					semaphore.trySetPermits(skus.getSeckillCount().intValue());
				}
			});

		});
	}


	@Override
	public String kill(String killId, String key, Integer num) {
		// 登录信息
		MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
		// 获取当前秒杀商品的详细信息
		BoundHashOperations<String, String, String> ops = redisTemplate.boundHashOps(SKUKILL_CACHE_PREFIX);
		String json = ops.get(killId);
		if (StringUtils.isEmpty(json)) {
			return null;
		} else {
			SecKillSkuRedisTo redisTo = JSON.parseObject(json, SecKillSkuRedisTo.class);
			// 校验合法性
			Long startTime = redisTo.getStartTime();
			Long endTime = redisTo.getEndTime();
			Long nowTime = new Date().getTime();
			Long ttl = endTime - startTime;
			if (nowTime >= startTime && nowTime <= endTime) {
				// 校验随机码
				String randomCode = redisTo.getRandomCode();
				String Id = redisTo.getPromotionSessionId() + "_" + redisTo.getSkuId();
				if (randomCode.equals(key) && killId.equals(Id)) {
					// 验证购买数是否合理
					if (num <= redisTo.getSeckillLimit().intValue()) {
						// 验证这个人是否已经购买过。幂等性；如果只要秒杀成功，就去占位。
						String redisKey = memberRespVo.getId() + "_" + Id;
						// 自动过期
							RSemaphore semaphore = redissonClient.getSemaphore(SKU_STOCK_SEMAPHORE + randomCode);
							try {
								boolean b = semaphore.tryAcquire(num, 100, TimeUnit.MILLISECONDS);
								if (b) {
									Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(redisKey, num.toString(), ttl, TimeUnit.MILLISECONDS);
									// 占位成功
									String timeId = IdWorker.getTimeId();
									SeckillOrderTo seckillOrderTo = new SeckillOrderTo();
									seckillOrderTo.setOrderSn(timeId);
									seckillOrderTo.setMemberId(memberRespVo.getId());
									seckillOrderTo.setNum(num);
									seckillOrderTo.setPromotionSessionId(redisTo.getPromotionSessionId());
									seckillOrderTo.setSkuId(redisTo.getSkuId());
									seckillOrderTo.setSeckillPrice(redisTo.getSeckillPrice());
									seckillOrderTo.setSkuPic(redisTo.getSkuInfo().getSkuDefaultImg());
									rabbitTemplate.convertAndSend("order-event-exchange", "order.seckill.order", seckillOrderTo);
									return timeId;
								} else {
									return null;
								}
							} catch (InterruptedException e) {
								e.printStackTrace();
								return null;
							}

					}
				} else {
					return null;
				}
				return null;
			} else {
				return null;
			}
		}

	}
}
